﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using log4net;
using System.Web.Security;

namespace TsCore
{
    internal class MatchHandler
    {
        private static MatchHandler _instance;        
        public List<Fixture> Generators { get; set; }

        public static MatchHandler Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new MatchHandler();
                    _instance.initFixtures();
                }
                return _instance;
            }
        }

        public void CreateNewMatch(DateTime Date, Team HomeTeam, Team VisitorTeam)
        {
            if (Date == null)
            {
                Date = (System.DateTime.Now).AddDays(1);
            }

            try
            {
                Match match = new Match(Date, HomeTeam, VisitorTeam);
                List<Match> matches = new List<Match>();
                matches.Add(match);
                AddNewMatches(matches);
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al crear un evento: HomeTeam: " + HomeTeam.Name + " VisitorTeam: " + VisitorTeam.Name + " Date: " + Date, ex);
            }
        }

        private void initFixtures()
        {
            //Para agregar nuevos fixtures solo basta crear la clase y agregarla a la lista aqui
            Generators = new List<Fixture>();
            Generators.Add(new SimpleFixture());
            Generators.Add(new RandomFixture());
            for (int i=0; i< Generators.Count; i++)
                Generators[i].FixtureId = i;            
        }

        public List<Fixture> GetFixtureGenerators()
        {
            return Generators;
        }

        public List<Match> GetAllMatches()
        {
            List<Match> retList = new List<Match>();
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    retList = (from s in ctx.Matches.Include("MediaList")
                               select s).ToList<Match>();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al obtener los Encuentros", ex);
            }
            return retList;
        }

        public List<Match> GetAllMatchesWithTeams()
        {
            List<Match> retList = new List<Match>();
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    retList = (from s in ctx.Matches.Include("HomeTeam").Include("VisitorTeam").Include("MediaList")
                               select s).ToList<Match>();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al obtener los Encuentros", ex);
            }
            return retList;
        }

        public void AutoGenerateMatches(DateTime FromDate, Fixture FixtureGen, List<Team> Teams)
        {
            List<Match> matches = FixtureGen.GenerateMatches(FromDate,Teams);
            try
            {
                AddNewMatches(matches);
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al agregar los encuentros autogenerados", ex);
            }
            //LOG exito
        }
        
        public void AddNewMatches(List<Match> Matches)
        {
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    foreach (Match m in Matches)
                    {
                        if (ValidateMatch(m))
                        {

                            if (m.HomeTeam.TeamId == 0)
                            {
                                Sport toAttachSport = m.HomeTeam.Sport;
                                ctx.Sports.Attach(toAttachSport);
                                ctx.Teams.Add(m.HomeTeam);
                            }
                            else
                            {
                                if (ctx.Entry(m.HomeTeam).State == System.Data.EntityState.Detached)
                                {
                                    ctx.Teams.Attach(m.HomeTeam);
                                }
                            }
                            if (m.VisitorTeam.TeamId == 0)
                            {
                                ctx.Teams.Add(m.VisitorTeam);
                            }
                            else
                            {
                                if (ctx.Entry(m.VisitorTeam).State == System.Data.EntityState.Detached)
                                {
                                    ctx.Teams.Attach(m.VisitorTeam);
                                }
                            }
                            ctx.Matches.Add(m);
                        }
                        else
                        {
                            Exception ex = new Exception("El encuentro" + m.ToString() + "no es válido");
                            throw ex;
                        }
                    }
                    ctx.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al agregar los encuentros autogenerados", ex);
            }           
        }
        
        private Boolean ValidateMatch(Match TheMatch)
        {
            Boolean isValid = (TheMatch != null && TheMatch.HomeTeam != null && TheMatch.VisitorTeam != null &&
                              TheMatch.VisitorTeam != TheMatch.HomeTeam);

            List<Match> matches = GetAllMatches();

            if (matches.Count > 0)
            {
                var check =
                    from m in matches
                    where m.Date == TheMatch.Date && (m.HomeTeam == TheMatch.VisitorTeam || m.HomeTeam == TheMatch.HomeTeam ||
                                                     m.VisitorTeam == TheMatch.VisitorTeam || m.VisitorTeam == TheMatch.HomeTeam)
                    select m;

                isValid &= (check.Distinct().Count() == 0);
            }
            return isValid;
        }

        public void updateMatchModified(List<Match> Matches)
        {
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    foreach (Match m in Matches)
                    {
                        if (ValidateMatch(m))
                        {
                            Match originalMatch = (from game in ctx.Matches.Include("HomeTeam").Include("VisitorTeam").Include("MediaList")
                                                   where game.MatchId == m.MatchId
                                                   select game).SingleOrDefault();

                            Team newHomeTeam = (from t in ctx.Teams
                                                where t.TeamId == m.HomeTeam.TeamId
                                                select t).SingleOrDefault();

                            Team newVisitorteam = (from t in ctx.Teams
                                                   where t.TeamId == m.VisitorTeam.TeamId
                                                   select t).SingleOrDefault();

                            DateTime newDate = m.Date;
                            Int32 newWinner = m.Winner;

                            if (ctx.Entry(m).State == System.Data.EntityState.Detached)
                            {
                                ctx.Matches.Attach(originalMatch);

                                originalMatch.HomeTeam = newHomeTeam;
                                originalMatch.VisitorTeam = newVisitorteam;
                                originalMatch.Date = newDate;
                                originalMatch.Winner = newWinner;
                                if (m.MediaList != null)
                                {
                                    originalMatch.MediaList = m.MediaList;
                                }

                                ctx.Teams.Attach(originalMatch.HomeTeam);
                                ctx.Teams.Attach(originalMatch.VisitorTeam);
                            }
                        }
                        else
                        {
                            Exception ex = new Exception("El encuentro" + m.ToString() + "no es válido");
                            throw ex;
                        }
                    }
                    ctx.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al actualizar encuentros", ex);
            }
        }

        public List<Match> GetMatchesForTeam(Team team)
        {
            List<Match> toReturn = new List<Match>();

            foreach (Match auxMatch in this.GetAllMatchesWithTeams())
            {
                if (auxMatch.HomeTeam != null && auxMatch.VisitorTeam != null)
                {
                    if (auxMatch.HomeTeam.Equals(team) || auxMatch.VisitorTeam.Equals(team))
                    {
                        if (!(toReturn.Contains(auxMatch)))
                        {
                            toReturn.Add(auxMatch);
                        }
                    }
                }
            }
            return toReturn;
        }

        public Match GetMatch(Int32 MatchKey)
        {
            Match retMatch = null;
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    retMatch = (from s in ctx.Matches.Include("HomeTeam").Include("VisitorTeam").Include("MediaList")
                                where s.MatchId == MatchKey
                                select s).SingleOrDefault();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al obtener el encuentro con key: " + MatchKey, ex);
            }
            return retMatch;
        }

        public List<Match> GetSportMatches(int SportId)
        {
            List<Match> retList = new List<Match>();
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    retList = (from s in ctx.Matches.Include("HomeTeam")
                               where s.HomeTeam.Sport.SportId == SportId
                               select s).ToList<Match>();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al obtener los Encuentros de un Deporte: " + SportId, ex);
            }

            return retList;
        }

        public DataTable GetCalendarForDate(int Month, int Year, List<Sport> SportList)
        {
            DateTime CalendarDate = new DateTime(Year, Month, 1);
            DataTable retTable = new DataTable();
            retTable.Columns.Add("SportID");
            retTable.Columns.Add("Deporte");
            while (CalendarDate.Month == Month)
            {
                retTable.Columns.Add(CalendarDate.Day.ToString());
                CalendarDate = CalendarDate.AddDays(1);
            }

            foreach (Sport s in SportList)
            {
                string[] newRow = new string[retTable.Columns.Count];
                newRow[0] = s.SportId.ToString();
                newRow[1] = s.Name;
                List<Match> MatchesList = GetSportMatches(s.SportId);
                
                foreach (Match m in MatchesList)
                {
                    if (m.Date.Month == Month)
                    {
                        if (newRow[m.Date.Day + 1] == null)
                            newRow[m.Date.Day + 1] = "0";
                        newRow[m.Date.Day + 1] = (Int32.Parse(newRow[m.Date.Day + 1]) + 1).ToString();
                    }
                }
                retTable.Rows.Add(newRow);
            }
            return retTable;
        }

        public List<Match> GetMatchesForDateAndSport(DateTime MatchDate, int SportId)
        {
            List<Match> retList = new List<Match>();
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    retList = (from s in ctx.Matches.Include("HomeTeam").Include("VisitorTeam")
                               where s.HomeTeam.Sport.SportId == SportId && s.Date == MatchDate
                               select s).ToList<Match>();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al obtener los Encuentros de un Deporte para una Fecha: " + SportId + "/" + MatchDate.ToShortDateString(), ex);
            }
            return retList;
        }

        internal void UpdateMatch(Match updatedMatch)
        {
            List<Match> matches = new List<Match>();
            matches.Add(updatedMatch);

            try
            {
                updateMatchModified(matches);
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al actualizar un Encuentro", ex);
            }
        }

        public List<Position> GetPositionTable(Sport sport)
        {
            List<Position> toReturn = new List<Position>();

            foreach (Team auxTeam in TsSystem.Instance.GetTeamsForSport(sport.SportId))
            {
                if (!(auxTeam.IsDummyTeam))
                {
                    Position newPosition = new Position();
                    newPosition.Team = auxTeam;

                    toReturn.Add(newPosition);
                }
            }

            foreach (Match auxMatch in this.GetAllMatchesWithTeams())
            {
                //Si es null es que no se jugo el encuentro.
                if (auxMatch.Winner >= 0)
                {
                    foreach (Position auxPosition in toReturn)
                    {
                        //Si es mayor a 0, el numero del winner es el TeamId del ganador, si es 0 es empate
                        if (auxMatch.Winner > 0)
                        {
                            if (auxPosition.Team.TeamId == auxMatch.Winner)
                            {
                                auxPosition.Points = auxPosition.Points + 3;
                            }
                        }
                        //El else significa que es 0, ya que no utilizamos numeros negativos y ya se controla si es null
                        else
                        {
                            if ((auxPosition.Team.TeamId == auxMatch.HomeTeam.TeamId) || (auxPosition.Team.TeamId == auxMatch.VisitorTeam.TeamId))
                            {
                                auxPosition.Points = auxPosition.Points + 1;
                            }
                        }
                    }
                }
            }

            toReturn.Sort();

            for (int i = 0; i < toReturn.Count; i++)
            {
                toReturn.ElementAt(i).TeamPosition = i + 1;
            }
            return toReturn;
        }

        public void RemoveMatch(Match match)
        {
            try
            {
                using (TsContext ctx = new TsContext())
                {
                    ctx.Matches.Attach(match);
                    ctx.Matches.Remove(match);
                    ctx.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Membership.GetUser().UserName).Error("Error al eliminar un Encuentro: " + match.MatchId, ex);
            }
        }
    }
}
